package com.miniserver.tql.database.impl;

import com.miniserver.tql.database.EngineContext;
import com.miniserver.tql.database.model.TAttr;
import com.miniserver.tql.database.model.TNode;

public class SqlBuilder {
	private static TNode getChild(TNode parent, String childName) {
		TNode tNode = null;
		TNode[] childNodes = parent.getChildren();
		for (TNode tChild : childNodes) {
			if (tChild.getNodeName().equals(childName)) {
				tNode = tChild;
				break;
			}
		}
		return tNode;
	}

	private static void appendBinderParams(TNode tNode, Binder binder) {
		TAttr tBinder = tNode.getAttribute("bind");
		if (tBinder != null) {
			String[] params = tBinder.getValue().split(";");
			for (String s : params) {
				binder.getParams().add(s);
			}
		}
	}

	private static String applyWhere(EngineContext context, TNode tNode, Binder binder) {
		TNode tWhereNode = getChild(tNode, "where");
		if (tWhereNode == null) {
			return "";
		}
		//
		TAttr tHadWhere = tWhereNode.getAttribute("had");
		boolean hadWhere = (tHadWhere != null) && tHadWhere.getValue().equals("true");
		StringBuilder whereBuilder = new StringBuilder();
		TNode[] tWhereChildren = tWhereNode.getChildren();
		for (TNode tBool : tWhereChildren) {
			applyBooleanExpr(context, tBool, binder, whereBuilder);
		}
		//
		if (whereBuilder.length() == 0) {
			return "";
		}
		//
		if (hadWhere) {
			return whereBuilder.toString();
		} else {
			if (whereBuilder.charAt(1) == 'A') { // " AND"
				return " where " + whereBuilder.substring(4);
			} else { // OR
				return " where " + whereBuilder.substring(3);
			}
		}
	}

	private static void applyBooleanExpr(EngineContext context, TNode tBool, Binder binder, StringBuilder whereBuilder) {
		TAttr tExpr = tBool.getAttribute("expr");
		if (tExpr == null) {
			return;
		}
		//
		boolean isOpt = false;
		TAttr tOpt = tBool.getAttribute("opt");
		if (tOpt != null && tOpt.getValue().equalsIgnoreCase("true")) {
			isOpt = true;
		}
		//
		if (isOpt && !optVarsExists(context, tBool)) {
			return;
		}

		appendBinderParams(tBool, binder);
		String whereExpr = Binder.applyReplace(context, tBool, tExpr.getValue());
		whereBuilder.append(' ').append(tBool.getNodeName().toUpperCase());
		whereBuilder.append(" (").append(whereExpr);
		//
		TNode[] tList = tBool.getChildren();
		for (TNode tItem : tList) {
			applyBooleanExpr(context, tItem, binder, whereBuilder);
		}
		whereBuilder.append(") ");
	}

	private static boolean optVarsExists(EngineContext context, TNode tNode) {
		boolean contain = true;
		TAttr tBind = tNode.getAttribute("bind");
		if (tBind != null) {
			String[] vars = tBind.getValue().split(";");
			for (String s : vars) {
				if (!Binder.paramExists(context, s)) {
					contain = false;
					break;
				}
			}
		}
		if (!contain) {
			return false;
		}
		//
		TAttr tReplace = tNode.getAttribute("replace");
		if (tReplace != null) {
			String[] vars = tReplace.getValue().split(";");
			for (String s : vars) {
				if (!Binder.paramExists(context, s)) {
					contain = false;
					break;
				}
			}
		}
		return contain;
	}

	public static Binder prepareQuery(EngineContext context, TNode tNode) throws Exception {
		Binder binder = new Binder();
		StringBuilder sqlBuilder = new StringBuilder();
		// 处理"select columns from table"
		TAttr tSQL = tNode.getAttribute("sql");
		if (tSQL != null) {
			sqlBuilder.append(tSQL.getValue());
		} else {
			TAttr tTable = tNode.getAttribute("table");
			if (tTable == null) {
				throw new Exception("Attribute \"sql\"  missing.");
			}
			//
			TAttr tColumns = tNode.getAttribute("columns");
			if (tColumns != null) {
				sqlBuilder.append("select ").append(tColumns.getValue());
				sqlBuilder.append(" from ").append(tTable.getValue());
			} else {
				sqlBuilder.append("select * from ").append(tTable.getValue());
			}
		}
		//
		appendBinderParams(tNode, binder);
		// 处理Where
		String where = applyWhere(context, tNode, binder);
		int pos = sqlBuilder.indexOf("order by");
		if (pos > 0) {
			sqlBuilder.insert(pos, where);
		} else {
			sqlBuilder.append(where);
		}
		// 处理replace属性值
		String sql = sqlBuilder.toString();
		sql = Binder.applyReplace(context, tNode, sql);
		// 处理is NULL
		sql = Binder.replaceNullCols(context, tNode, sql, binder);
		//
		binder.setSql(sql);
		return binder;
	}

	public static Binder prepareUpdate(EngineContext context, TNode tNode) throws Exception {
		Binder binder = new Binder();
		StringBuilder sqlBuilder = new StringBuilder();
		// 处理"select columns from table"
		TAttr tSQL = tNode.getAttribute("sql");
		if (tSQL != null) {
			sqlBuilder.append(tSQL.getValue());
		} else {
			TAttr tTable = tNode.getAttribute("table");
			if (tTable == null) {
				throw new Exception("Attribute \"sql\"  missing.");
			}
			sqlBuilder.append("update ").append(tTable.getValue()).append(" set ");
		}
		// 更新可选列
		TAttr tCols = tNode.getAttribute("optCols");
		TAttr tBinds = tNode.getAttribute("optBinds");
		if (tCols != null && tBinds != null) {
			int idx = 0;
			int pos = sqlBuilder.indexOf(" set ") + 5;
			int pos1 = sqlBuilder.indexOf(" where ");
			if (pos1 < 0) {
				pos1 = sqlBuilder.length() - 1;
			}
			boolean hadUpdate = pos + 1 < pos1;
			if (hadUpdate) {
				String setList = sqlBuilder.substring(pos, pos1);
				if (setList.indexOf('=') < 0) {
					hadUpdate = false;
				}
			}
			String[] cols = tCols.getValue().split(";");
			String[] binds = tBinds.getValue().split(";");
			for (String s : binds) {
				if (!Binder.paramExists(context, s)) {
					idx++;
					continue;
				}
				//
				sqlBuilder.insert(pos, cols[idx] + "=?,");
				binder.getParams().add(s);
				//
				pos += cols[idx].length() + 3;
				idx++;
			}
			//
			if (!hadUpdate) {
				sqlBuilder.replace(pos - 1, pos, " ");
			}
		}
		// 绑定的参数必须在可选列后面
		appendBinderParams(tNode, binder);
		// 处理Where
		String where = applyWhere(context, tNode, binder);
		sqlBuilder.append(where);
		// 处理replace属性值
		String sql = sqlBuilder.toString();
		sql = Binder.applyReplace(context, tNode, sql);
		// 处理is NULL
		sql = Binder.replaceNullCols(context, tNode, sql, binder);
		//
		binder.setSql(sql);
		return binder;
	}
}
